﻿using System.Collections.Generic;
using UnityEngine;


namespace XYRendering.Vegetation
{

    public abstract class IVGInstancingMaterialManager
    {
        public abstract Material GetLodMaterial(Material baseMaterial, int lodLevel);

        public virtual void OnInit() {}

        public virtual void OnPreRendering() {}

        public virtual void OnPostRendering() {}
        
        public virtual void OnDestory() {}
    }
    
    
    
    //
    // 通过shader keyword 控制不同LOD material的渲染复杂度， 美术无法干预
    //
    public class VGInstancingMaterialManager : IVGInstancingMaterialManager
    {
        public static int s_MaxLodLevels = 3;
        
        public static string[] s_LodKeyWords = new[] {"", "_VGINSTANCING_LOD1", "_VGINSTANCING_LOD2"};

        private Dictionary<int, Dictionary<int, Material>> _lodMaterials = new Dictionary<int, Dictionary<int, Material>>();

        private Dictionary<int, bool> _updatedLodMaterials = new Dictionary<int, bool>();

        public override Material GetLodMaterial(Material baseMaterial, int lodLevel)
        {
            int lod = Mathf.Clamp(lodLevel, 0, s_MaxLodLevels-1);

            if (lod == 0)
                return baseMaterial;
            
            if (!_lodMaterials.TryGetValue(lod, out var curLodMaterils))
            {
                curLodMaterils = new Dictionary<int, Material>();
                _lodMaterials.Add(lod, curLodMaterils);
            }

            int baseId = baseMaterial.GetInstanceID();
            if (!curLodMaterils.TryGetValue(baseId, out var lodMat))
            {
                lodMat = UnityEngine.Object.Instantiate(baseMaterial);
                lodMat.hideFlags = HideFlags.HideAndDontSave;
                curLodMaterils.Add(baseId, lodMat);
            }

  
            int lodId = lodMat.GetInstanceID();
            if (!_updatedLodMaterials.TryGetValue(lodId, out var updated) || !updated)
            {
                lodMat.CopyPropertiesFromMaterial(
                    baseMaterial); // copy all material properties in case of animation
                lodMat.EnableKeyword(s_LodKeyWords[lod]);
                _updatedLodMaterials[lodId] = true;
            }

            return lodMat;
        }

        public override void OnPreRendering()
        {   
            _updatedLodMaterials.Clear();
        }

        public override void OnDestory() 
        {
            foreach (var mats in _lodMaterials.Values)
            {
                foreach (var mat in mats.Values)
                {
#if UNITY_EDITOR
                    UnityEngine.Object.DestroyImmediate(mat);
#else
                    UnityEngine.Object.Destroy(mat);
#endif
                }   
            }
            _lodMaterials.Clear();
            _updatedLodMaterials.Clear();
        }
    }
}